// 680x0 (Sixty Eight K) Interface
#include "burnint.h"

int nSekM68KContextSize[SEK_MAX];
char* SekM68KContext[SEK_MAX];

int nSekCount = -1;							// Number of allocated 68000s
struct SekExt *SekExt[SEK_MAX] = { NULL, }, *pSekExt = NULL;

int nSekActive = -1;								// The cpu which is currently being emulated
int nSekCyclesTotal, nSekCyclesScanline, nSekCyclesSegment, nSekCyclesDone, nSekCyclesToDo;

int nSekCPUType[SEK_MAX], nSekCycles[SEK_MAX], nSekIRQPending[SEK_MAX];

// ----------------------------------------------------------------------------
// Default memory access handlers

unsigned char __fastcall DefReadByte(unsigned int) { return 0; }
void __fastcall DefWriteByte(unsigned int, unsigned char) { }

#define DEFWORDHANDLERS(i)																				\
	unsigned short __fastcall DefReadWord##i(unsigned int a) { SEK_DEF_READ_WORD(i, a) }				\
	void __fastcall DefWriteWord##i(unsigned int a, unsigned short d) { SEK_DEF_WRITE_WORD(i, a ,d) }
#define DEFLONGHANDLERS(i)																				\
	unsigned int __fastcall DefReadLong##i(unsigned int a) { SEK_DEF_READ_LONG(i, a) }					\
	void __fastcall DefWriteLong##i(unsigned int a, unsigned int d) { SEK_DEF_WRITE_LONG(i, a , d) }

DEFWORDHANDLERS(0);
DEFLONGHANDLERS(0);

#if SEK_MAXHANDLER >= 2
 DEFWORDHANDLERS(1);
 DEFLONGHANDLERS(1);
#endif

#if SEK_MAXHANDLER >= 3
 DEFWORDHANDLERS(2);
 DEFLONGHANDLERS(2);
#endif

#if SEK_MAXHANDLER >= 4
 DEFWORDHANDLERS(3);
 DEFLONGHANDLERS(3);
#endif

#if SEK_MAXHANDLER >= 5
 DEFWORDHANDLERS(4);
 DEFLONGHANDLERS(4);
#endif

#if SEK_MAXHANDLER >= 6
 DEFWORDHANDLERS(5);
 DEFLONGHANDLERS(5);
#endif

#if SEK_MAXHANDLER >= 7
 DEFWORDHANDLERS(6);
 DEFLONGHANDLERS(6);
#endif

#if SEK_MAXHANDLER >= 8
 DEFWORDHANDLERS(7);
 DEFLONGHANDLERS(7);
#endif

// ----------------------------------------------------------------------------
// Memory access functions

// Mapped Memory lookup (               for read)
#define FIND_R(x) pSekExt->MemMap[ x >> SEK_SHIFT]
// Mapped Memory lookup (+ SEK_WADD     for write)
#define FIND_W(x) pSekExt->MemMap[(x >> SEK_SHIFT) + SEK_WADD]
// Mapped Memory lookup (+ SEK_WADD * 2 for fetch)
#define FIND_F(x) pSekExt->MemMap[(x >> SEK_SHIFT) + SEK_WADD * 2]

// Normal memory access functions
static inline unsigned char ReadByte(unsigned int a)
{
	unsigned char* pr;

	a &= 0xFFFFFF;

	pr = FIND_R(a);
	if ((unsigned int)pr >= SEK_MAXHANDLER) {
		a ^= 1;
		return pr[a & SEK_PAGEM];
	}
	return pSekExt->ReadByte[(unsigned int)pr](a);
}

static inline unsigned char FetchByte(unsigned int a)
{
	unsigned char* pr;

	a &= 0xFFFFFF;

	pr = FIND_F(a);
	if ((unsigned int)pr >= SEK_MAXHANDLER) {
		a ^= 1;
		return pr[a & SEK_PAGEM];
	}
	return pSekExt->ReadByte[(unsigned int)pr](a);
}

static inline void WriteByte(unsigned int a, unsigned char d)
{
	unsigned char* pr;

	a &= 0xFFFFFF;

	pr = FIND_W(a);
	if ((unsigned int)pr >= SEK_MAXHANDLER) {
		a ^= 1;
		pr[a & SEK_PAGEM] = (unsigned char)d;
		return;
	}
	pSekExt->WriteByte[(unsigned int)pr](a, d);
}

static inline void WriteByteROM(unsigned int a, unsigned char d)
{
	unsigned char* pr;

	a &= 0xFFFFFF;

	pr = FIND_R(a);
	if ((unsigned int)pr >= SEK_MAXHANDLER) {
		a ^= 1;
		pr[a & SEK_PAGEM] = (unsigned char)d;
		return;
	}
	pSekExt->WriteByte[(unsigned int)pr](a, d);
}

static inline unsigned short ReadWord(unsigned int a)
{
	unsigned char* pr;

	a &= 0xFFFFFF;

	pr = FIND_R(a);
	if ((unsigned int)pr >= SEK_MAXHANDLER) {
		return *((unsigned short*)(pr + (a & SEK_PAGEM)));
	}
	return (pSekExt->ReadWord[(unsigned int)pr](a));
}

static inline unsigned short FetchWord(unsigned int a)
{
	unsigned char* pr;

	a &= 0xFFFFFF;

	pr = FIND_F(a);
	if ((unsigned int)pr >= SEK_MAXHANDLER) {
		return *((unsigned short*)(pr + (a & SEK_PAGEM)));
	}
	return (pSekExt->ReadWord[(unsigned int)pr](a));
}

static inline void WriteWord(unsigned int a, unsigned short d)
{
	unsigned char* pr;

	a &= 0xFFFFFF;

	pr = FIND_W(a);
	if ((unsigned int)pr >= SEK_MAXHANDLER) {
		*((unsigned short*)(pr + (a & SEK_PAGEM))) = (unsigned short)d;
		return;
	}
	pSekExt->WriteWord[(unsigned int)pr](a, d);
}

static inline void WriteWordROM(unsigned int a, unsigned short d)
{
	unsigned char* pr;

	a &= 0xFFFFFF;

	pr = FIND_R(a);
	if ((unsigned int)pr >= SEK_MAXHANDLER) {
		*((unsigned short*)(pr + (a & SEK_PAGEM))) = (unsigned short)d;
		return;
	}
	pSekExt->WriteWord[(unsigned int)pr](a, d);
}

static inline unsigned int ReadLong(unsigned int a)
{
	unsigned char* pr;

	a &= 0xFFFFFF;

	pr = FIND_R(a);
	if ((unsigned int)pr >= SEK_MAXHANDLER) {
		unsigned int r = *((unsigned int*)(pr + (a & SEK_PAGEM)));
		r = (r >> 16) | (r << 16);
		return r;
	}
	return (pSekExt->ReadLong[(unsigned int)pr](a));
}

static inline unsigned int FetchLong(unsigned int a)
{
	unsigned char* pr;

	a &= 0xFFFFFF;

	pr = FIND_F(a);
	if ((unsigned int)pr >= SEK_MAXHANDLER) {
		unsigned int r = *((unsigned int*)(pr + (a & SEK_PAGEM)));
		r = (r >> 16) | (r << 16);
		return r;
	}
	return (pSekExt->ReadLong[(unsigned int)pr](a));
}

static inline void WriteLong(unsigned int a, unsigned int d)
{
	unsigned char* pr;

	a &= 0xFFFFFF;

	pr = FIND_W(a);
	if ((unsigned int)pr >= SEK_MAXHANDLER) {
		d = (d >> 16) | (d << 16);
		*((unsigned int*)(pr + (a & SEK_PAGEM))) = d;
		return;
	}
	pSekExt->WriteLong[(unsigned int)pr](a, d);
}

static inline void WriteLongROM(unsigned int a, unsigned int d)
{
	unsigned char* pr;

	a &= 0xFFFFFF;

	pr = FIND_R(a);
	if ((unsigned int)pr >= SEK_MAXHANDLER) {
		d = (d >> 16) | (d << 16);
		*((unsigned int*)(pr + (a & SEK_PAGEM))) = d;
		return;
	}
	pSekExt->WriteLong[(unsigned int)pr](a, d);
}

// ----------------------------------------------------------------------------
// Memory accesses (non-emu specific)

extern "C" {
unsigned int __fastcall M68KReadByte(unsigned int a) { return (unsigned int)ReadByte(a); }
unsigned int __fastcall M68KReadWord(unsigned int a) { return (unsigned int)ReadWord(a); }
unsigned int __fastcall M68KReadLong(unsigned int a) { return               ReadLong(a); }

unsigned int __fastcall M68KFetchByte(unsigned int a) { return (unsigned int)FetchByte(a); }
unsigned int __fastcall M68KFetchWord(unsigned int a) { return (unsigned int)FetchWord(a); }
unsigned int __fastcall M68KFetchLong(unsigned int a) { return               FetchLong(a); }

void __fastcall M68KWriteByte(unsigned int a, unsigned int d) { WriteByte(a, d); }
void __fastcall M68KWriteWord(unsigned int a, unsigned int d) { WriteWord(a, d); }
void __fastcall M68KWriteLong(unsigned int a, unsigned int d) { WriteLong(a, d); }
}

unsigned int SekReadByte(unsigned int a) { return (unsigned int)ReadByte(a); }
unsigned int SekReadWord(unsigned int a) { return (unsigned int)ReadWord(a); }
unsigned int SekReadLong(unsigned int a) { return ReadLong(a); }

unsigned int SekFetchByte(unsigned int a) { return (unsigned int)FetchByte(a); }
unsigned int SekFetchWord(unsigned int a) { return (unsigned int)FetchWord(a); }
unsigned int SekFetchLong(unsigned int a) { return FetchLong(a); }

void SekWriteByte(unsigned int a, unsigned char d) { WriteByte(a, d); }
void SekWriteWord(unsigned int a, unsigned short d) { WriteWord(a, d); }
void SekWriteLong(unsigned int a, unsigned int d) { WriteLong(a, d); }

void SekWriteByteROM(unsigned int a, unsigned char d) { WriteByteROM(a, d); }
void SekWriteWordROM(unsigned int a, unsigned short d) { WriteWordROM(a, d); }
void SekWriteLongROM(unsigned int a, unsigned int d) { WriteLongROM(a, d); }

// ----------------------------------------------------------------------------
// Callbacks for Musashi

extern "C" int M68KIRQAcknowledge(int nIRQ)
{
	if (nSekIRQPending[nSekActive] & SEK_IRQSTATUS_AUTO) {
		m68k_set_irq(0);
		nSekIRQPending[nSekActive] = 0;
	}

#if 1
	// Enable IrqCallback for M68K
	// by OopsWare 2007.11
	if (pSekExt->IrqCallback) {
		return pSekExt->IrqCallback(nIRQ);
	}
#else
	if (pSekExt->IrqCallback) {
		pSekExt->IrqCallback(nIRQ);
	}
#endif

	return M68K_INT_ACK_AUTOVECTOR;
}

extern "C" void M68KResetCallback()
{
	if (pSekExt->ResetCallback) {
		pSekExt->ResetCallback();
	}
}

extern "C" void M68KRTECallback()
{
	if (pSekExt->RTECallback) {
		pSekExt->RTECallback();
	}
}

extern "C" void M68KcmpildCallback(unsigned int val, int reg)
{
	if (pSekExt->CmpCallback) {
		pSekExt->CmpCallback(val, reg);
	}
}

// ----------------------------------------------------------------------------
// Initialisation/exit/reset

static int SekInitCPUM68K(int nCount, int nCPUType)
{
	nSekCPUType[nCount] = nCPUType;

	switch (nCPUType) {
		case 0x68000:
			m68k_set_cpu_type(M68K_CPU_TYPE_68000);
			break;
		case 0x68010:
			m68k_set_cpu_type(M68K_CPU_TYPE_68010);
			break;
		case 0x68EC020:
			m68k_set_cpu_type(M68K_CPU_TYPE_68EC020);
			break;
		default:
			return 1;
	}

	nSekM68KContextSize[nCount] = m68k_context_size();
	SekM68KContext[nCount] = (char*)malloc(nSekM68KContextSize[nCount]);
	if (SekM68KContext[nCount] == NULL) {
		return 1;
	}
	memset(SekM68KContext[nCount], 0, nSekM68KContextSize[nCount]);
	m68k_get_context(SekM68KContext[nCount]);

	return 0;
}

void SekNewFrame()
{
	for (int i = 0; i <= nSekCount; i++) {
		nSekCycles[i] = 0;
	}

	nSekCyclesTotal = 0;
}

void SekSetCyclesScanline(int nCycles)
{
	nSekCyclesScanline = nCycles;
}

int SekInit(int nCount, int nCPUType)
{
	struct SekExt* ps = NULL;

	if (nSekActive >= 0) {
		SekClose();
		nSekActive = -1;
	}

	if (nCount > nSekCount) {
		nSekCount = nCount;
	}

	// Allocate cpu extenal data (memory map etc)
	SekExt[nCount] = (struct SekExt*)malloc(sizeof(struct SekExt));
	if (SekExt[nCount] == NULL) {
		SekExit();
		return 1;
	}
	memset(SekExt[nCount], 0, sizeof(struct SekExt));

	// Put in default memory handlers
	ps = SekExt[nCount];

	for (int j = 0; j < SEK_MAXHANDLER; j++) {
		ps->ReadByte[j]  = DefReadByte;
		ps->WriteByte[j] = DefWriteByte;
	}

	ps->ReadWord[0]  = DefReadWord0;
	ps->WriteWord[0] = DefWriteWord0;
	ps->ReadLong[0]  = DefReadLong0;
	ps->WriteLong[0] = DefWriteLong0;

#if SEK_MAXHANDLER >= 2
	ps->ReadWord[1]  = DefReadWord1;
	ps->WriteWord[1] = DefWriteWord1;
	ps->ReadLong[1]  = DefReadLong1;
	ps->WriteLong[1] = DefWriteLong1;
#endif

#if SEK_MAXHANDLER >= 3
	ps->ReadWord[2]  = DefReadWord2;
	ps->WriteWord[2] = DefWriteWord2;
	ps->ReadLong[2]  = DefReadLong2;
	ps->WriteLong[2] = DefWriteLong2;
#endif

#if SEK_MAXHANDLER >= 4
	ps->ReadWord[3]  = DefReadWord3;
	ps->WriteWord[3] = DefWriteWord3;
	ps->ReadLong[3]  = DefReadLong3;
	ps->WriteLong[3] = DefWriteLong3;
#endif

#if SEK_MAXHANDLER >= 5
	ps->ReadWord[4]  = DefReadWord4;
	ps->WriteWord[4] = DefWriteWord4;
	ps->ReadLong[4]  = DefReadLong4;
	ps->WriteLong[4] = DefWriteLong4;
#endif

#if SEK_MAXHANDLER >= 6
	ps->ReadWord[5]  = DefReadWord5;
	ps->WriteWord[5] = DefWriteWord5;
	ps->ReadLong[5]  = DefReadLong5;
	ps->WriteLong[5] = DefWriteLong5;
#endif

#if SEK_MAXHANDLER >= 7
	ps->ReadWord[6]  = DefReadWord6;
	ps->WriteWord[6] = DefWriteWord6;
	ps->ReadLong[6]  = DefReadLong6;
	ps->WriteLong[6] = DefWriteLong6;
#endif

#if SEK_MAXHANDLER >= 8
	ps->ReadWord[7]  = DefReadWord7;
	ps->WriteWord[7] = DefWriteWord7;
	ps->ReadLong[7]  = DefReadLong7;
	ps->WriteLong[7] = DefWriteLong7;
#endif

#if SEK_MAXHANDLER >= 9
	for (int j = 8; j < SEK_MAXHANDLER; j++) {
		ps->ReadWord[j]  = DefReadWord0;
		ps->WriteWord[j] = DefWriteWord0;
		ps->ReadLong[j]  = DefReadLong0;
		ps->WriteLong[j] = DefWriteLong0;
	}
#endif

	m68k_init();
	if (SekInitCPUM68K(nCount, nCPUType)) {
		SekExit();
		return 1;
	}

	nSekCycles[nCount] = 0;
	nSekIRQPending[nCount] = 0;

	nSekCyclesTotal = 0;
	nSekCyclesScanline = 0;

	CpuCheatRegister(CPU_M68K, nCount);

	return 0;
}

static void SekCPUExitM68K(int i)
{
	free(SekM68KContext[i]);
	SekM68KContext[i] = NULL;
}

int SekExit()
{
	// Deallocate cpu extenal data (memory map etc)
	for (int i = 0; i <= nSekCount; i++) {
		SekCPUExitM68K(i);

		// Deallocate other context data
		free(SekExt[i]);
		SekExt[i] = NULL;
	}

	pSekExt = NULL;

	nSekActive = -1;
	nSekCount = -1;

	return 0;
}

void SekReset()
{
	m68k_pulse_reset();
}

// ----------------------------------------------------------------------------
// Control the active CPU

// Open a CPU
void SekOpen(const int i)
{
	if (i != nSekActive) {
		nSekActive = i;

		pSekExt = SekExt[nSekActive];						// Point to cpu context

		m68k_set_context(SekM68KContext[nSekActive]);

		nSekCyclesTotal = nSekCycles[nSekActive];
	}
}

// Close the active cpu
void SekClose()
{
	m68k_get_context(SekM68KContext[nSekActive]);

	nSekCycles[nSekActive] = nSekCyclesTotal;
}

// Get the current CPU
int SekGetActive()
{
	return nSekActive;
}

// Set the status of an IRQ line on the active CPU
void SekSetIRQLine(const int line, const int status)
{
//	bprintf(PRINT_NORMAL, ("  - irq line %i -> %i\n"), line, status);

	if (status) {
		nSekIRQPending[nSekActive] = line | status;

		m68k_set_irq(line);

		return;
	}

	nSekIRQPending[nSekActive] = 0;

	m68k_set_irq(0);
}

// Adjust the active CPU's timeslice
void SekRunAdjust(const int nCycles)
{
	if (nCycles < 0 && m68k_ICount < -nCycles) {
		SekRunEnd();
		return;
	}

	nSekCyclesToDo += nCycles;
	m68k_modify_timeslice(nCycles);
}

// End the active CPU's timeslice
void SekRunEnd()
{
	m68k_end_timeslice();
}

// Run the active CPU
int SekRun(const int nCycles)
{
	if (nCycles <= 0) return 0;

	nSekCyclesToDo = nCycles;

	nSekCyclesSegment = m68k_execute(nCycles);

	nSekCyclesTotal += nSekCyclesSegment;
	nSekCyclesToDo = m68k_ICount = -1;

	return nSekCyclesSegment;
}

// ----------------------------------------------------------------------------
// Memory map setup

// Note - each page is 1 << SEK_BITS.
int SekMapMemory(unsigned char* pMemory, unsigned int nStart, unsigned int nEnd, int nType)
{
	unsigned char* Ptr = pMemory - nStart;
	unsigned char** pMemMap = pSekExt->MemMap + (nStart >> SEK_SHIFT);

	// Special case for ROM banks
	if (nType == SM_ROM) {
		for (unsigned int i = (nStart & ~SEK_PAGEM); i <= nEnd; i += SEK_PAGE_SIZE, pMemMap++) {
			pMemMap[0]			  = Ptr + i;
			pMemMap[SEK_WADD * 2] = Ptr + i;
		}

		return 0;
	}

	for (unsigned int i = (nStart & ~SEK_PAGEM); i <= nEnd; i += SEK_PAGE_SIZE, pMemMap++) {

		if (nType & SM_READ) {					// Read
			pMemMap[0]			  = Ptr + i;
		}
		if (nType & SM_WRITE) {					// Write
			pMemMap[SEK_WADD]	  = Ptr + i;
		}
		if (nType & SM_FETCH) {					// Fetch
			pMemMap[SEK_WADD * 2] = Ptr + i;
		}
	}

	return 0;
}

int SekMapHandler(unsigned int nHandler, unsigned int nStart, unsigned int nEnd, int nType)
{
	unsigned char** pMemMap = pSekExt->MemMap + (nStart >> SEK_SHIFT);

	// Add to memory map
	for (unsigned int i = (nStart & ~SEK_PAGEM); i <= nEnd; i += SEK_PAGE_SIZE, pMemMap++) {

		if (nType & SM_READ) {					// Read
			pMemMap[0]			  = (unsigned char*)nHandler;
		}
		if (nType & SM_WRITE) {					// Write
			pMemMap[SEK_WADD]	  = (unsigned char*)nHandler;
		}
		if (nType & SM_FETCH) {					// Fetch
			pMemMap[SEK_WADD * 2] = (unsigned char*)nHandler;
		}
	}

	return 0;
}

// Set callbacks
int SekSetResetCallback(pSekResetCallback pCallback)
{
	pSekExt->ResetCallback = pCallback;

	return 0;
}

int SekSetRTECallback(pSekRTECallback pCallback)
{
	pSekExt->RTECallback = pCallback;

	return 0;
}

int SekSetIrqCallback(pSekIrqCallback pCallback)
{
	pSekExt->IrqCallback = pCallback;

	return 0;
}

int SekSetCmpCallback(pSekCmpCallback pCallback)
{
	pSekExt->CmpCallback = pCallback;

	return 0;
}

// Set handlers
int SekSetReadByteHandler(int i, pSekReadByteHandler pHandler)
{
	if (i >= SEK_MAXHANDLER) {
		return 1;
	}

	pSekExt->ReadByte[i] = pHandler;

	return 0;
}

int SekSetWriteByteHandler(int i, pSekWriteByteHandler pHandler)
{
	if (i >= SEK_MAXHANDLER) {
		return 1;
	}

	pSekExt->WriteByte[i] = pHandler;

	return 0;
}

int SekSetReadWordHandler(int i, pSekReadWordHandler pHandler)
{
	if (i >= SEK_MAXHANDLER) {
		return 1;
	}

	pSekExt->ReadWord[i] = pHandler;

	return 0;
}

int SekSetWriteWordHandler(int i, pSekWriteWordHandler pHandler)
{
	if (i >= SEK_MAXHANDLER) {
		return 1;
	}

	pSekExt->WriteWord[i] = pHandler;

	return 0;
}

int SekSetReadLongHandler(int i, pSekReadLongHandler pHandler)
{
	if (i >= SEK_MAXHANDLER) {
		return 1;
	}

	pSekExt->ReadLong[i] = pHandler;

	return 0;
}

int SekSetWriteLongHandler(int i, pSekWriteLongHandler pHandler)
{
	if (i >= SEK_MAXHANDLER) {
		return 1;
	}

	pSekExt->WriteLong[i] = pHandler;

	return 0;
}

// ----------------------------------------------------------------------------
// Query register values

int SekGetPC(int n)
{
	return m68k_get_reg(NULL, M68K_REG_PC);
}

int SekDbgGetCPUType()
{
	switch (nSekCPUType[nSekActive]) {
		case 0:
		case 0x68000:
			return M68K_CPU_TYPE_68000;
		case 0x68010:
			return M68K_CPU_TYPE_68010;
		case 0x68EC020:
			return M68K_CPU_TYPE_68EC020;
	}

	return 0;
}

int SekDbgGetPendingIRQ()
{
	return nSekIRQPending[nSekActive] & 7;
}

// ----------------------------------------------------------------------------
// Savestate support

int SekScan(int nAction)
{
	// Scan the 68000 states
	if ((nAction & ACB_DRIVER_DATA) == 0) {
		return 1;
	}

	struct BurnArea ba;
	memset(&ba, 0, sizeof(ba));

	nSekActive = -1;

	for (int i = 0; i <= nSekCount; i++) {
		char szName[] = "MC68000 #n";
		szName[9] = '0' + i;

		SCAN_VAR(nSekCPUType[i]);

		if (nSekCPUType[i] != 0) {
			ba.Data = SekM68KContext[i];
			ba.nLen = nSekM68KContextSize[i];
			ba.szName = szName;
			BurnAcb(&ba);
		}
	}

	return 0;
}
